package com.luo.sc.oidc.authserver.handler.kapatcha;

import com.google.code.kaptcha.Producer;
import com.luo.sc.oidc.authserver.enums.KaptchaTypeEnum;
import org.springframework.lang.Nullable;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

/**
 * Kapatcha生成器 - 代理类
 *
 * @author luohq
 * @version 1.0.0
 * @date 2022-03-14 14:35
 */
public class DelegatingKapatchaProducer {

    /**
     * 文本验证码生成器
     */
    private Producer kaptchaTextProducer;

    /**
     * 算术表达式验证码生成器
     */
    private Producer kaptchaMathProducer;

    /**
     * 验证码结果保存服务
     */
    private KapatchResultSaveService kapatchResultSaveService = new DefaultKaptchaResultSaveServiceImpl();

    /**
     * 构造函数
     *
     * @param kaptchaTextProducer      文本验证码生成器
     * @param kaptchaMathProducer      算术表达式验证码生成器
     * @param kapatchResultSaveService 验证码结果保存服务
     */
    public DelegatingKapatchaProducer(Producer kaptchaTextProducer, Producer kaptchaMathProducer, @Nullable KapatchResultSaveService kapatchResultSaveService) {
        this.kaptchaTextProducer = kaptchaTextProducer;
        this.kaptchaMathProducer = kaptchaMathProducer;
        if (null != kapatchResultSaveService) {
            this.kapatchResultSaveService = kapatchResultSaveService;
        }
    }

    /**
     * 创建文本验证码
     *
     * @return 验证码结果
     */
    public KaptchaResultDto createTextKaptcha() {
        String renderText = this.kaptchaTextProducer.createText();
        return KaptchaResultDto.builder()
                .renderText(renderText)
                .resultText(renderText)
                .kaptchaImage(this.kaptchaTextProducer.createImage(renderText))
                .build();
    }

    /**
     * 创建算术表达式验证码
     *
     * @return 验证码结果
     */
    public KaptchaResultDto createMathKaptcha() {
        //拆分算术表达式和结果
        String mathText = this.kaptchaMathProducer.createText();
        String renderText = mathText.substring(0, mathText.lastIndexOf(KaptchaMathExpTextCreator.MATH_SEPARATOR));
        String resultText = mathText.substring(mathText.lastIndexOf(KaptchaMathExpTextCreator.MATH_SEPARATOR) + 1);
        return KaptchaResultDto.builder()
                .renderText(renderText)
                .resultText(resultText)
                .kaptchaImage(this.kaptchaMathProducer.createImage(renderText))
                .build();
    }

    /**
     * 根据类型创建验证码
     *
     * @param type 验证码类型（1:文本，2:算法表达式）
     * @return 验证码结果
     */
    public KaptchaResultDto createKaptchByType(Integer type) {
        if (KaptchaTypeEnum.TEXT.getCode().equals(type)) {
            return this.createTextKaptcha();
        } else if (KaptchaTypeEnum.MATH.getCode().equals(type)) {
            return this.createMathKaptcha();
        }
        return this.createTextKaptcha();
    }

    /**
     * 随机创建验证码（文本 或 算术表达式）
     *
     * @return 验证码结果
     */
    public KaptchaResultDto createRandomTypeKaptcha() {
        Integer randomType = new Random().nextInt(2) + 1;
        return this.createKaptchByType(randomType);
    }


    /**
     * 生成文本验证码并通过response将验证码图片返回给终端
     *
     * @param request  请求对象
     * @param response 响应对象
     */
    public void responseWriteTextKapatch(HttpServletRequest request, HttpServletResponse response) {
        this.responseWriteKapatch(request, response, this.createTextKaptcha());
    }

    /**
     * 生成算术表达式验证码并通过response将验证码图片返回给终端
     *
     * @param request  请求对象
     * @param response 响应对象
     */
    public void responseWriteMathKapatch(HttpServletRequest request, HttpServletResponse response) {
        this.responseWriteKapatch(request, response, this.createMathKaptcha());
    }

    /**
     * 根据类型生成验证码并通过response将验证码图片返回给终端
     *
     * @param type     验证码类型（1:文本，2:算法表达式）
     * @param request  请求对象
     * @param response 响应对象
     */
    public void responseWriteKapatchByType(Integer type, HttpServletRequest request, HttpServletResponse response) {
        this.responseWriteKapatch(request, response, this.createKaptchByType(type));
    }

    /**
     * 生成算术表达式验证码并通过response将验证码图片返回给终端
     *
     * @param request  请求对象
     * @param response 响应对象
     */
    public void responseWriteRandowmTypeKapatch(HttpServletRequest request, HttpServletResponse response) {
        this.responseWriteKapatch(request, response, this.createRandomTypeKaptcha());
    }

    /**
     * 通过response将验证码图片返回给终端
     *
     * @param request  请求对象
     * @param response 响应对象
     */
    public void responseWriteKapatch(HttpServletRequest request, HttpServletResponse response, KaptchaResultDto kaptchaResultDto) {
        //缓存验证码结果
        this.kapatchResultSaveService.saveResult(kaptchaResultDto.getResultText(), request, response);
        //通过response返回验证码图片
        this.responseWriteKaptchaImage(response, kaptchaResultDto.getKaptchaImage());
    }

    /**
     * 通过response写验证码图片
     *
     * @param response     响应对象
     * @param kaptchaImage 验证码图片
     */
    public void responseWriteKaptchaImage(HttpServletResponse response, BufferedImage kaptchaImage) {
        ServletOutputStream out = null;
        try {
            //设置响应头
            response.setDateHeader("Expires", 0);
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            response.addHeader("Cache-Control", "post-check=0, pre-check=0");
            response.setHeader("Pragma", "no-cache");
            response.setContentType("image/jpeg");
            //将验证码图片写出响应流
            out = response.getOutputStream();
            ImageIO.write(kaptchaImage, "jpg", out);
            out.flush();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 验证结果文本是否匹配（默认验证匹配成功后即清除缓存的结果文本）
     *
     * @param resultText 验证码结果文本
     * @param request    请求对象
     * @param response   响应对象
     * @return true匹配，false则不匹配
     */
    public Boolean verifyKaptchaResultText(String resultText, HttpServletRequest request, HttpServletResponse response) {
        return this.kapatchResultSaveService.verifyResult(resultText, request, response);
    }

    /**
     * 验证结果文本是否匹配
     *
     * @param resultText       验证码结果文本
     * @param request          请求对象
     * @param response         响应对象
     * @param clearCacheResult 清除之前缓存的验证码结果文本
     * @return true匹配，false则不匹配
     */
    Boolean verifyKaptchaResultText(String resultText, HttpServletRequest request, HttpServletResponse response, Boolean clearCacheResult) {
        return this.kapatchResultSaveService.verifyResult(resultText, request, response, clearCacheResult);
    }
}
